/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package wumpusworld;

import java.awt.Point;
import javax.swing.JOptionPane;
/**
 *
 * @author Jedrek
 */
public class WumpusEngine {

    public static final int DIRECTION_LEFT = 1;
    public static final int DIRECTION_RIGHT = 2;
    public static final int DIRECTION_UP = 4;
    public static final int DIRECTION_DOWN = 8;

    private MyCanvas canvas = null;
    private int[][] map = null;
    private int mapSizeX = 8;
    private int mapSizeY = 8;
    private boolean initialized = false;
    
    private Point agentPosition = new Point(0, 0);
    private boolean goldFound = false;
    private boolean dragonKilled = false;
    private boolean agentAlive = false;
    private boolean shootingEnabled = false;
    private boolean agentWon = false;

    public boolean showNotifications = true;
    public boolean disposeWin = true;
    
    /**
     * set this to false if you don't want engine to create it's own map on initialization
     * (then you have to provide your own map before initialization)
     */
    public boolean generateMapOnInit = true;
    /**
     * default constructor
     */
    public WumpusEngine(){
        
    }
    
    /**
     * initializes the game (creates the map, sets initial position of the agent, etc.)
     * @return true if the game was succesfully initialized
     */
    public boolean init(){
        boolean enteranceFound = false;
        
        initialized = false;
        goldFound = false;
        dragonKilled = false;
        agentAlive = false;
        shootingEnabled = false;
        agentWon = false;
        
        if( generateMapOnInit )
        {
            MapGenerator mapGen = new MapGenerator();
            mapGen.sizeX = mapSizeX;
            mapGen.sizeY = mapSizeY;
            map = mapGen.createMap();
        } else if( map==null ){
            return false;
        }
        
        if( map==null )
            return false;
        
        //look for enterance
        for( int i=0; i<map.length; i++ )
            for( int j=0; j<map[i].length; j++ ){
                if( (map[i][j] & (MyCanvas.ENTERENCE_DOWN | MyCanvas.ENTERENCE_LEFT | MyCanvas.ENTERENCE_RIGHT | MyCanvas.ENTERENCE_UP)) != 0 ){
                    moveAgent(i, j);
                    //this will end the loops:
                    enteranceFound = true;
                }
            }
        
        if(!enteranceFound)
            return false;
        
        if( canvas!=null ){
            canvas.setMap(map);
            canvas.agentPosition = agentPosition;
            canvas.drawAgent = true;
            canvas.repaint();
        }
        
        initialized = true;
        agentAlive = true;
        shootingEnabled = true;
        return true;
    }
    
    /**
     * 
     * @return true if the game was succesfully initialized
     */
    public boolean isInitialized(){
        return initialized;
    }
    
    /**
     * 
     * @return true if the agent has already found the gold 
     */
    public boolean isGoldFound(){
        return goldFound;
    }
    
    /**
     * 
     * @return true if the dragon was already killed
     */
    public boolean isDragonKilled(){
        return dragonKilled;
    }
    
    /**
     * @return true if agent is alive
     */
    public boolean isAgentAlive(){
        return agentAlive;
    }

    /**
     * @return true if agent found the gold and reached the enterance
     */
    public boolean isAgentWinner(){
        return agentWon;
    }

    /**
     * @return true if agent can shoot
     */
    public boolean canShoot(){
        return shootingEnabled;
    }
    
    /**
     * returns agent's position.
     * Returned point is the clone of the original object
     * and cannot be used to influence agent's position.
     * @return agent's position
     */
    public Point getAgentPosition(){
        return (Point)agentPosition.clone();
    }
    
    /**
     * sets the canvas on which the game state will be drawn
     * @param canvas 
     */
    public void setCanvas(MyCanvas canvas){
        this.canvas = canvas;
        if(canvas!=null){
            canvas.setMap(map);
            canvas.repaint();
        }
    }
    
    /**
     * retrives canvas on which game state is beeing drawn
     * @return canvas
     */
    public MyCanvas getCanvas(){
        return canvas;
    }
    
    /**
     * sets the map of the world. Normally the map is created on initialization (init() method)
     * After calling this method you have to initialize the game.
     * @param map new map
     */
    public void setMap(int[][] map){
        this.map = map;
        initialized = false;
    }
    
    /**
     * Retrives state of the (x,y) cell of the map
     * @param x coordinate
     * @param y coordinate
     * @return -1 in case of error, HIDDEN if the cell is hidden, real state of the cell
     * if there is no error and the cell is not hidden
     */
    public int getCellState(int x, int y){
        if(!initialized)
            return -1;
        if(x<0 || x>=map.length)
            return -1;
        if(y<0 || y>=map[x].length)
            return -1;
        
        if( (map[x][y] & MyCanvas.HIDDEN)!=0 )
            return MyCanvas.HIDDEN;
        return map[x][y];
    }
    
    /**
     * calls getCellState method for agent's position
     * @return value returned by getCellState method
     */
    public int getAgentCellState(){
        return getCellState(agentPosition.x, agentPosition.y);
    }

    public Point getCurrentMapSize(){
        if(map == null)
            return new Point(0, 0);
        
        return new Point(map.length, map[0].length);
    }
    
    /**
     * sets new size of the map. This value will be taken into consideration on
     * initialization. 
     * @param x new x dimension
     * @return true if new dimension was accepted (new x should be>=3)
     */
    public boolean setMapSizeX(int x){
        if(x>=3)
            mapSizeX = x;
        else
            return false;
        
        return true;
    }

    /**
     * preforms a shoot in specific direction
     * @param direction one of DIRECTION_LEFT, DIRECTION_RIGHT, DIRECTION_DOWN, DIRECTION_UP
     * @return true if wumpus is killed after shooting
     */
    public boolean shoot(int direction){
        switch(direction){
            case DIRECTION_DOWN:
                for(int i = agentPosition.y+1; i<map[agentPosition.x].length; i++){
                    if( (map[agentPosition.x][i] & MyCanvas.WUMPUS) != 0){
                        map[agentPosition.x][i] &= ~MyCanvas.WUMPUS;
                        dragonKilled = true;
                        if(canvas != null)
                            canvas.repaint();
                        return true;
                    }
                }
                break;
            case DIRECTION_UP:
                for(int i = agentPosition.y-1; i>=0; i--){
                    if( (map[agentPosition.x][i] & MyCanvas.WUMPUS) != 0){
                        map[agentPosition.x][i] &= ~MyCanvas.WUMPUS;
                        dragonKilled = true;
                        if(canvas != null)
                            canvas.repaint();
                        return true;
                    }
                }
                break;
            case DIRECTION_LEFT:
                for(int i = agentPosition.y-1; i>=0; i--){
                    if( (map[i][agentPosition.y] & MyCanvas.WUMPUS) != 0){
                       map[i][agentPosition.y] &= ~MyCanvas.WUMPUS;
                        dragonKilled = true;
                        if(canvas != null)
                            canvas.repaint();
                        return true;
                    }
                }
                break;
            case DIRECTION_RIGHT:
                for(int i = agentPosition.y+1; i<map.length; i++){
                    if( (map[i][agentPosition.y] & MyCanvas.WUMPUS) != 0){
                        map[i][agentPosition.y] &= ~MyCanvas.WUMPUS;
                        dragonKilled = true;
                        if(canvas != null)
                            canvas.repaint();
                        return true;
                    }
                }
                break;
            default:
                break;
        }
        if(dragonKilled)
            return true;
        return false;
    }
    
    /**
     * Returns x-dimension which was used while creating map
     * @return x-dimension
     */
    public int getMapSizeX(){
        return mapSizeX;
    }
    
    /**
     * sets new size of the map. This value will be taken into consideration on
     * initialization. 
     * @param y new y dimension
     * @return true if new dimension was accepted (new y should be>=3)
     */
    public boolean setMapSizeY(int y){
        if(y>=3)
            mapSizeY = y;
        else
            return false;
        
        return true;
    }
    
    
    /**
     * Returns y-dimension which was used while creating map
     * @return y-dimension
     */
    public int getMapSizeY(){
        return mapSizeY;
    }
    
    /**
     * makes move to the left
     * @return effect of the move (-1 if not succesfull, otherwise the state of the new cell is revealed)
     */
    public int moveLeft(){
        if(!initialized)
            return -1;
        if(!agentAlive)
            return -1;
        if(agentPosition.x>0){
            return moveAgent(agentPosition.x-1, agentPosition.y);
        }
        return -1;
    }
    
    /**
     * makes move to the right
     * @return effect of the move (-1 if not succesfull, otherwise the state of the new cell is revealed)
     */
    public int moveRight(){
        if(!initialized)
            return -1;
        if(!agentAlive)
            return -1;
        if(agentPosition.x<map.length-1){
            return moveAgent(agentPosition.x+1, agentPosition.y);
        }
        return -1;
    }
    
    /**
     * makes move up
     * @return effect of the move (-1 if not succesfull, otherwise the state of the new cell is revealed)
     */
    public int moveUp(){
        if(!initialized)
            return -1;
        if(!agentAlive)
            return -1;
        if(agentPosition.y>0){
            return moveAgent(agentPosition.x, agentPosition.y-1);
        }
        return -1;
    }
    
    /**
     * makes move down
     * @return effect of the move (-1 if not succesfull, otherwise the state of the new cell is revealed)
     */
    public int moveDown(){
        if(!initialized)
            return -1;
        if(!agentAlive)
            return -1;
        if(agentPosition.y<map[agentPosition.x].length-1){
            return moveAgent(agentPosition.x, agentPosition.y+1);
        }
        return -1;
    }

    /**
     * makes move in specified direction
     * @param direction one of DIRECTION_LEFT, DIRECTION_RIGHT, ...
     * @return effect of the move (-1 if not succesfull, otherwise the state of the new cell is revealed)
     */
    public int move(int direction){
        switch(direction){
            case DIRECTION_LEFT:
                return moveLeft();
            case DIRECTION_RIGHT:
                return moveRight();
            case DIRECTION_UP:
                return moveUp();
            case DIRECTION_DOWN:
                return moveDown();
            default:
                return -1;
        }
    }
    
    private int moveAgent(Point newLoc){
        return moveAgent(newLoc.x, newLoc.y);
    }
    
    private int moveAgent(int x, int y){
        if( map==null )
            return -1;
        if(x<0 || x>=map.length)
            return -1;
        if(y<0 || y>=map[x].length)
            return -1;
        
        agentPosition.setLocation(x, y);
        map[x][y] &= ~MyCanvas.HIDDEN;
        
        if( (map[x][y] & (MyCanvas.PIT | MyCanvas.WUMPUS))!= 0)
            agentAlive = false;

        if( (map[x][y] & MyCanvas.GOLD) != 0){
            goldFound = true;
            if(showNotifications)
                JOptionPane.showMessageDialog(null, "You found the gold");
            else
                System.out.println("You found the gold");
            map[x][y] &= ~MyCanvas.GOLD;
            map[x][y] |= MyCanvas.CHEST;
        }

        if( goldFound && (map[x][y] & (MyCanvas.ENTERENCE_DOWN |
                                       MyCanvas.ENTERENCE_LEFT |
                                       MyCanvas.ENTERENCE_RIGHT |
                                       MyCanvas.ENTERENCE_UP)) != 0){
            agentWon = true;
        }
        
        if( canvas!=null )
            canvas.repaint();

        if(agentWon){
            if(showNotifications)
                JOptionPane.showMessageDialog(null, "Grats! You won!");
            else
                System.out.println("Grats! You won!");
            if(disposeWin)
                ControlWindow.mainWindow.dispose();
//            ControlWindow.closeGame();

        }
        return map[x][y];
    }

    
}
